package com.david.service;

import cn.dev33.satoken.stp.StpUtil;
import com.david.config.MyException;
import com.david.dao.UserInfoMapper;
import com.david.entity.Goods;
import com.david.entity.Order;
import com.david.utils.RedisCache;
import com.david.utils.RedisUtils;
import com.david.vo.ResponseBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @description: 秒杀业务层
 * @create: 2021-04-26-15-28
 * @author: David
 **/
@Service
@Slf4j
public class SecKillService {

    @Resource
    RedisUtils redisUtils;

    @Resource
    RedisCache cacheService;

    @Resource
    RedisTemplate redisTemplate;

    @Resource
    DefaultMQProducer defaultMQProducer;

    @Resource
    UserInfoMapper userInfoMapper;

    /**
     * 开始秒杀
     * @param order 包含userId和orderId，指代用户以及商品id
     */
    public ResponseBean secKill(Order order) throws Exception {

        // 使用Lua脚本精确判断用户是否重复请求
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        List<String> keys1 = new ArrayList<>();
        keys1.add(order.getUserId());
        String user = " local tmp = redis.call('hexists', 'user', KEYS[1]);"
                + " if (tmp == 0) then "
                + "        redis.call('hset', 'user', KEYS[1], '1');"
                + "        return 1;"
                + " end;"
                + " if (tmp == 1) then"
                + "        return -1;"
                + " end;"
                + " return 0;";
        script.setScriptText(user);
        script.setResultType(long.class);
        String execute1 = Objects.requireNonNull(redisTemplate.execute(script, keys1)).toString();
        // 判断是否重复请求
        if(Integer.parseInt(execute1) == -1){
            log.info("用户 " + StpUtil.getLoginId() + "被检测到发生了重复购买行为");
            throw new MyException("抢购排队中，请勿重复点击抢购");
        }

        // 初步判断库存是否没了，拦截大部分请求
        if(Integer.parseInt(redisUtils.get("Goods" + order.getOrderId()).toString()) <= 0){
            throw new MyException("库存已售空");
        }
        // 使用Lua脚本精确判断库存是否没有，没有就抛出异常、有就原子操作减一
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        List<String> keys = new ArrayList<>();
        keys.add("Goods1");
        String lua = "if (redis.call('exists', KEYS[1]) == 1) then" +
                "    local stock = tonumber(redis.call('get', KEYS[1]));" +
                "    if (stock == -1) then" +
                "        return -1;" +
                "    end;" +
                "    if (stock >= 1) then" +
                "        return tonumber(redis.call('incrby', KEYS[1], -1));" +
                "    end;" +
                "    return -2;" +
                "end;" +
                "return -3;";
        redisScript.setScriptText(lua);
        redisScript.setResultType(long.class);
        String execute = Objects.requireNonNull(redisTemplate.execute(redisScript, keys)).toString();
        if(Integer.parseInt(execute) < 0){
            throw new MyException("商品已售罄！");
        }

        // 将用户请求存入消息队列中，此为提交订单模块锁定库存
        String msg = order.getOrderId() + order.getUserId();
        Message delaySendMsg = new Message("HelloTopic", "DelayTag", msg.getBytes());
        Message sendMsg = new Message("HelloTopic", "NormalTag", msg.getBytes());
        // 发送延时消息，为了方便测试设置超时时间为10s，用来清理未付款的账户
        delaySendMsg.setDelayTimeLevel(3);
        defaultMQProducer.send(sendMsg);
        defaultMQProducer.send(delaySendMsg);

        // 此处跳转向支付页面
        return payOrder(order);

    }

    /**
     * 秒杀前时间的查询以及商品地址的返回以及商品预热
     * @param id 查询的商品id
     * @return 返回时间或者秒杀地址
     */
    public ResponseBean entrance(Integer id){
        ResponseBean responseBean = new ResponseBean();
        Goods goods = cacheService.findGoods(id);
        Date curTime = new Date();
        Date startTime = goods.getCreateTime();

        redisUtils.hset("user", "-1", "1");
        redisUtils.set("Goods" + goods.getId(), goods.getNumber());

        Map<String, String> temp = new HashMap<>(2);

        if(curTime.before(startTime)){
            responseBean.setCode(0);
            responseBean.setMessage("抢购未开始！");
            temp.put("time", startTime.toString());
        }else{
            responseBean.setCode(1);
            responseBean.setMessage("抢购已经开始！");
            temp.put("url", "/secKill");
            responseBean.setObj(temp);
        }

        return responseBean;
    }

    public ResponseBean payOrder(Order order) throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);
        double key = Math.random();
        StpUtil.checkLogin();
        // 90%的几率付款成功，然后数据库订单状态变为1
        if(key > 0.1){
            Order order1 = userInfoMapper.findOrder(Integer.parseInt(order.getUserId()));
            if(order1.getPay() == 0){
                userInfoMapper.payOrder(Integer.parseInt(order.getUserId()));
            }else {
                return ResponseBean.error("请勿重复付款！", 200);
            }
            return ResponseBean.error("您已付款成功！", 200);
        }else{
            return ResponseBean.error("您未选择付款！", 200);
        }
    }

}
